package com.source;

import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

/**
 * Created by Samuel on 16/8/1.
 */
public class SElastic {
    /**
     * ES 搜索引擎地址
     */
    private  static String ip = ControlPro.es_ip;
    /**
     * ES 搜索引擎端口号
     */
    private  static int port = ControlPro.es_port;

    /**
     * 增加 索引／类型／id／数据
     * @param bean 数据对象
     */
    public static void addDocument(SElasticBase bean) {
        if (!bean.checkNullPrivate() && bean.getJsonMap()!=null){
            return;
        }
        try {
            Client client = TransportClient
                    .builder()
                    .build()
                    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(ip), port));
            IndexResponse response = client
                    .prepareIndex(bean.index, bean.type, bean.id)
                    .setSource(bean.getJsonMap())
                    .get();
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 批量增加 索引／类型／id／数据
     * @param beanList 数据对象数组
     */
    public static void addDocuments(List<SElasticBase> beanList) {
        try {
            Client client = TransportClient.builder().build().addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(ip), port));
            BulkRequestBuilder bulkRequest = client.prepareBulk();
            for (SElasticBase bean:beanList){
                if (!bean.checkNullPrivate() || bean.getJsonMap()==null){
                    return;
                }
                bulkRequest.add(client.prepareIndex(bean.index, bean.type, StringUtils.trim(bean.id)).setSource(bean.getJsonMap()));
            }
            BulkResponse bulkResponse = bulkRequest.get();
            if (bulkResponse.hasFailures()) {
                System.err.println("");
            }
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 根据 索引／类型／id 删除一条数据
     * @param bean 数据对象
     */
    public static void delDocument(SElasticBase bean) {
        if (!bean.checkNullPrivate() || bean.getJsonMap()==null){
            return;
        }
        try {
            Client client = TransportClient
                    .builder()
                    .build()
                    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(ip), port));
            DeleteResponse response = client.prepareDelete(bean.index,bean.type,bean.id).get();
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 根据  索引／类型／id 更新一条数据 里的 某个字段
     * （如果需要更新大量字段 建议使用addDocument方法）
     * @param bean
     */
    public static void updateDocument(SElasticBase bean) {
        if (!bean.checkNullPrivate()||
                bean.id==null ||
                bean.terms==null||
                bean.terms.size()<1 ||
                bean.terms.get(0).key==null ||
                bean.terms.get(0).value==null){
            return;
        }
        try {
            Client client = TransportClient
                    .builder()
                    .build()
                    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(ip), port));
            UpdateRequest updateRequest = new UpdateRequest(bean.index, bean.type, bean.id);
            updateRequest.doc(XContentFactory
                    .jsonBuilder()
                    .startObject()
                    .field(bean.terms.get(0).key, bean.terms.get(0).value)
                    .endObject());
            client.update(updateRequest).get();
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 根据 索引／类型／id 获取一条数据
     * @param bean 数据对象
     * @return
     */
    public static Map<String, Object> getDocument(SElasticBase bean) {
        if (!bean.checkNullPrivate() ){
            return null;
        }
        try {
            Client client = TransportClient
                    .builder()
                    .build()
                    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(ip), port));
            GetResponse response = client.prepareGet(bean.index, bean.type, bean.id).get();
            Map<String, Object> map = response.getSource();
            return map;
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 根据 索引／类型／value条件／key-value条件／范围条件 模糊检索数据（使用ik中文分词）
     * @param bean
     */
    public static List<Map<String, Object>> getDocuments_ik(SElasticBase bean) {
        if (bean.index==null || bean.type==null){
            return null;
        }
        try {
            Client client = TransportClient
                    .builder().build()
                    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(ip), port));
            //构造key-value查询条件（多条）
            BoolQueryBuilder bq = QueryBuilders.boolQuery();
            if (bean.terms != null && bean.terms.size()>0) {
                for (SElasticTerm elasticTerm : bean.terms) {
                    switch (elasticTerm.type){
                        case must:{
                            if (elasticTerm.isPhrase==true){
                                bq = bq.must(QueryBuilders.matchPhraseQuery(elasticTerm.key, elasticTerm.value));
                            }else {
                                bq = bq.must(QueryBuilders.matchQuery(elasticTerm.key, elasticTerm.value));
                            }
                            break;
                        }
                        case should:{
                            if (elasticTerm.isPhrase==true){
                                bq = bq.should(QueryBuilders.matchPhraseQuery(elasticTerm.key, elasticTerm.value));
                            }else {
                                bq = bq.should(QueryBuilders.matchQuery(elasticTerm.key, elasticTerm.value));
                            }
                            break;
                        }
                        case mustNot:{
                            if (elasticTerm.isPhrase==true){
                                bq = bq.mustNot(QueryBuilders.matchPhraseQuery(elasticTerm.key, elasticTerm.value));
                            }else {
                                bq = bq.mustNot(QueryBuilders.matchQuery(elasticTerm.key, elasticTerm.value));
                            }
                            break;
                        }
                        default:{
                            if (elasticTerm.isPhrase==true){
                                bq = bq.must(QueryBuilders.matchPhraseQuery(elasticTerm.key, elasticTerm.value));
                            }else {
                                bq = bq.must(QueryBuilders.matchQuery(elasticTerm.key, elasticTerm.value));
                            }
                            break;
                        }
                    }
                }
            }
            //构造 全文单string 查询参数
            if (bean.singleList!=null && bean.singleList.size()>0){
                for (SElasticSingle elasticSingle : bean.singleList) {
                    switch (elasticSingle.type){
                        case must:{
                            bq = bq.must(QueryBuilders.queryStringQuery(elasticSingle.value));
                            break;
                        }
                        case should:{
                            bq = bq.should(QueryBuilders.queryStringQuery(elasticSingle.value));
                            break;
                        }
                        case mustNot:{
                            bq = bq.mustNot(QueryBuilders.queryStringQuery(elasticSingle.value));
                            break;
                        }
                        default:{
                            bq = bq.must(QueryBuilders.queryStringQuery(elasticSingle.value));
                            break;
                        }
                    }
                }
            }
            //构造 范围 查询参数
            BoolQueryBuilder qb = QueryBuilders.boolQuery();
            if (bean.ranges != null && bean.ranges.size() > 0) {
                for (SElasticRange elasticRange : bean.ranges) {
                    if (elasticRange.key!=null && elasticRange.from!=null && elasticRange.to!=null) {
                        switch (elasticRange.type){
                            case must:{
                                qb = qb.must(QueryBuilders
                                        .rangeQuery(StringUtils.trim(elasticRange.key))//查询字段
                                        .from(StringUtils.trim(elasticRange.from))//开始边界
                                        .to(StringUtils.trim(elasticRange.to))//结束边界
                                        .includeLower(true)     //包括下界
                                        .includeUpper(true)); //包括上界
                                break;
                            }
                            case should:{
                                qb = qb.should(QueryBuilders
                                        .rangeQuery(StringUtils.trim(elasticRange.key))
                                        .from(StringUtils.trim(elasticRange.from))
                                        .to(StringUtils.trim(elasticRange.to))
                                        .includeLower(true)
                                        .includeUpper(true));
                                break;
                            }
                            case mustNot:{
                                qb = qb.mustNot(QueryBuilders
                                        .rangeQuery(StringUtils.trim(elasticRange.key))
                                        .from(StringUtils.trim(elasticRange.from))
                                        .to(StringUtils.trim(elasticRange.to))
                                        .includeLower(true)
                                        .includeUpper(true));
                                break;
                            }
                            default:{
                                qb = qb.must(QueryBuilders
                                        .rangeQuery(StringUtils.trim(elasticRange.key))
                                        .from(StringUtils.trim(elasticRange.from))
                                        .to(StringUtils.trim(elasticRange.to))
                                        .includeLower(true)
                                        .includeUpper(true));
                                break;
                            }
                        }
                    }
                }
            }
            //构造排序参数
            SortBuilder sortBuilder = null;
            if (bean.sorts != null && bean.sorts.size()>0) {
                for (SElasticSort elasticSort : bean.sorts) {
                    if (elasticSort.key==null){
                        return null;
                    }
                    sortBuilder = SortBuilders
                            .fieldSort(elasticSort.key)
                            .order(elasticSort.isASC_DESC==true?SortOrder.ASC : SortOrder.DESC);
                }
            }
            if (bean.pageSize==0){
                bean.pageSize = 10;
            }
            //构造 查询
            SearchRequestBuilder searchRequestBuilder = client
                    .prepareSearch(bean.index)//索引
                    .setTypes(bean.type)//类型
                    .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)//搜索方式 精确搜索（SCAN 无需扫描搜索）
                    .setQuery(bq) //搜索条件
                    .setFrom(bean.pageIndex)//分页 下标
                    .setSize(bean.pageSize)//分页 分页大小
                    .setPostFilter(qb) //范围 搜索条件
                    .addSort(sortBuilder)//排序条件
                    .setExplain(true);//返回搜索响应信息
            //构造 高亮 字段
            if (bean.fields != null && bean.fields.size() > 0) {
                for (String field : bean.fields) {
                    searchRequestBuilder.addHighlightedField(field);
                }
                searchRequestBuilder.setHighlighterEncoder("UTF-8").setHighlighterPreTags("<span style=\"color:red\">").setHighlighterPostTags("</span>");
            }
            else {
                bean.fields = new ArrayList<String>();
            }
            List<Map<String, Object>> lists = new ArrayList<Map<String, Object>>();
            //查询
            SearchResponse response = searchRequestBuilder.execute().actionGet();
            //取值
            SearchHits hits = response.getHits();
            for (SearchHit hit : hits) {
                Map<String, HighlightField> result = hit.highlightFields();
                for (String field : bean.fields) {
                    HighlightField titleField = result.get(field);
                    if (titleField == null) {
                        continue;
                    }
                    Text[] titleTexts = titleField.fragments();
                    String value = "";
                    for (Text text : titleTexts) {

                        value += text;
                    }
                    hit.getSource().put(field, value);
                }
                lists.add(hit.getSource());
            }
            return lists;
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;

    }
    public static List<Map<String, Object>> getDocumentsIkForAll(String index,
                                                                 String type,
                                                                 int from,
                                                                 int size,
                                                                 String allFliedString,
                                                                 List<String> fields,
                                                                 Map<String, String> mustMap,
                                                                 Map<String, String> shouldMap,
                                                                 Map<String, String> notMap,
                                                                 List<Map<String, String>> rangeList) {
        try {
            if (fields==null)
            {
                fields = new ArrayList<String>();
            }
            Client client = TransportClient.builder().build().addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(ip), port));
            List<Map<String, Object>> lists = new ArrayList<Map<String, Object>>();

            BoolQueryBuilder bb = QueryBuilders.boolQuery();
            if (allFliedString!=null)
            {
                bb = bb.must(QueryBuilders.queryStringQuery(allFliedString));
            }
            if (mustMap != null) {
                for (Object key : mustMap.keySet()) {
                    bb = bb.must(QueryBuilders.matchQuery((String) key, mustMap.get(key)));
                }
            }
            if (shouldMap != null) {
                for (Object key : shouldMap.keySet()) {
                    bb = bb.should(QueryBuilders.matchQuery((String) key, shouldMap.get(key)));
                }
            }
            if (notMap != null) {
                for (Object key : notMap.keySet()) {
                    bb = bb.mustNot(QueryBuilders.matchQuery((String) key, notMap.get(key)));
                }
            }

            //构造范围查询参数
            BoolQueryBuilder qb = QueryBuilders.boolQuery();
            if (rangeList != null && rangeList.size() > 0) {

                for (Map<String, String> map : rangeList) {
                    if (map != null && (!map.isEmpty())) {
                        qb = qb.must(QueryBuilders
                                .rangeQuery(StringUtils.trim(map.get("field")))
                                .from(StringUtils.trim(map.get("from")))
                                .to(StringUtils.trim(map.get("to")))
                                .includeLower(true)     //包括下界
                                .includeUpper(false)); //包括上界  ;
                    }
                }
            }


            //构造查询
            SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index)
                    .setTypes(type)
                    .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                    .setQuery(bb) // Query
                    .setFrom(from)
                    .setSize(size)
                    .setPostFilter(qb)
                    .setExplain(true);
            //构造高亮字段
            if (fields != null && fields.size() > 0) {
                for (String field : fields) {
                    searchRequestBuilder.addHighlightedField(field);
                }
                searchRequestBuilder.setHighlighterEncoder("UTF-8").setHighlighterPreTags("<span style=\"color:red\">").setHighlighterPostTags("</span>");
            }

            //查询
            SearchResponse response = searchRequestBuilder.execute().actionGet();

            //取值
            SearchHits hits = response.getHits();
            for (SearchHit hit : hits) {
                if (fields != null) {
                    Map<String, HighlightField> result = hit.highlightFields();
                    for (String field : fields) {
                        HighlightField titleField = result.get(field);
                        if (titleField == null) {
                            continue;
                        }
                        Text[] titleTexts = titleField.fragments();
                        String value = "";
                        for (Text text : titleTexts) {

                            value += text;
                        }
                        hit.getSource().put(field, value);
                    }
                }
                lists.add(hit.getSource());
            }
            return lists;
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;

    }
    /**
     *@Title: queryDocuments
     *@Description: TODO
     *@param @param index  相当于库s
     *@param @param type   想当于表
     *@param @param from 记录从哪开始
     *@param @param size 数量
     *@param @param rangeLists  范围 参数比如价格   key为   field,from,to
     *@param @param queryMaps  精确查询参数
     *@param @param sortMaps  排序参数  key为   field  value传大写的 ASC , DESC
     * *@param @param fields  要高亮的字段
     *@param @return
     *@return List<Map<String,Object>>
     *@throws
     */
    public static List<Map<String, Object>> queryDocuments(String index,
                                                           String type,
                                                           int from,
                                                           int size,
                                                           List<Map<Object, Object>> rangeLists,
                                                           Map<Object, Object> queryMaps,
                                                           Map<Object, Object> sortMaps,
                                                           List<String> fields,
                                                           Map<Object, Object> fullTextQueryMaps) {
        try {
            if (sortMaps ==null && fields==null)
            {
                return null;
            }
            Client client = TransportClient.builder().build().addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(ip), port));

            List<Map<String, Object>> lists = new ArrayList<Map<String, Object>>();
            /** 下面这一段是构造bool嵌套，就是构造一个在满足精确查找的条件下，再去进行多字段的或者关系的全文检索 **/
            //构造全文或关系的查询
            BoolQueryBuilder bb = QueryBuilders.boolQuery();
            if (fullTextQueryMaps != null) {
                for (Object key : fullTextQueryMaps.keySet()) {
                    bb = bb.should(QueryBuilders.matchQuery((String) key, fullTextQueryMaps.get(key)));
                }
            }

            //构造精确的并且查询
            BoolQueryBuilder bb1 = QueryBuilders.boolQuery();
            if (queryMaps != null) {
                bb1 = bb1.must(bb);
                for (Object key : queryMaps.keySet()) {
                    bb1 = bb1.must(QueryBuilders.termQuery((String) key, queryMaps.get(key)));
                }
            }
            /** 上面这一段是构造bool嵌套，就是构造一个在满足精确查找的条件下，再去进行多字段的或者关系的全文检索 **/
            //match全文检索，但是并且的关系， 或者的关系要用
            /*MatchQueryBuilder tq = null;
            if (queryMaps != null) {
                for (Object key : queryMaps.keySet()) {
                    tq = QueryBuilders.matchQuery((String) key, queryMaps.get(key));
                }
            }*/

            //term是代表完全匹配，即不进行分词器分析，文档中必须包含整个搜索的词汇
            /*  TermQueryBuilder tq = null;
                if (queryMaps != null) {
                    for (Object key : queryMaps.keySet()) {
                        tq = QueryBuilders.termQuery((String) key, queryMaps.get(key));
                    }
                }*/

            //构造范围查询参数
            QueryBuilder qb = null;
            if (rangeLists != null && rangeLists.size() > 0) {

                for (Map<Object, Object> map : rangeLists) {
                    if (map != null && (!map.isEmpty())) {
                        qb = QueryBuilders.rangeQuery(StringUtils.trim((String) map.get("field"))).from(StringUtils.trim((String)map.get("from"))).to(StringUtils.trim((String)map.get("to")));
                    }
                }
            }
            //构造排序参数
            SortBuilder sortBuilder = null;
            if (sortMaps != null) {
                for (Object key : sortMaps.keySet()) {
                    sortBuilder = SortBuilders
                            .fieldSort((String) key)
                            .order(StringUtils.trim((String)sortMaps.get(key))
                                    .equals("ASC") ? SortOrder.ASC : SortOrder.DESC);
                }
            }

            //构造查询
            SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index).setTypes(type).setSearchType(SearchType.DFS_QUERY_THEN_FETCH).setQuery(bb1) // Query
                    .setPostFilter(qb) // Filter
                    .setFrom(from).setSize(size).addSort(sortBuilder).setExplain(true);
            //构造高亮字段
            if (fields != null && fields.size() > 0) {
                for (String field : fields) {
                    searchRequestBuilder.addHighlightedField(field);
                }
                searchRequestBuilder.setHighlighterEncoder("UTF-8").setHighlighterPreTags("<span style=\"color:red\">").setHighlighterPostTags("</span>");
            }

            //查询
            SearchResponse response = searchRequestBuilder.execute().actionGet();

            //取值
            SearchHits hits = response.getHits();
            for (SearchHit hit : hits) {

                if (fields!=null)
                {
                    Map<String, HighlightField> result = hit.highlightFields();
                    for (String field : fields) {
                        HighlightField titleField = result.get(field);
                        if (titleField == null) {
                            continue;
                        }
                        Text[] titleTexts = titleField.fragments();
                        String value = "";
                        for (Text text : titleTexts) {

                            value += text;
                        }
                        hit.getSource().put(field, value);
                    }
                }
                lists.add(hit.getSource());

                //用高亮字段替换搜索字段

                //System.out.println(hit.getSource());
                //System.out.println(hit.getHighlightFields());
                //System.out.println(hit.getSourceAsString());//json格式

            }
            return lists;
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;

    }
}
